package behaviorTree

import (
	"fmt"
	"time"
)

type Invertor struct {
	*BaseNode
}

func NewInvertor() *Invertor {
	rlt := &Invertor{
		BaseNode: NewBaseNode(fmt.Sprintf("Invertor%d", GetNextId("Invertor")), NodeType_Invertor),
	}

	rlt.CheckValidationFunc = rlt.checkChild

	return rlt
}

func (self *Invertor) checkChild() (rlt bool, errInfo string) {
	rlt, errInfo = self.checkChildCnt(1)
	return
}
func (self *Invertor) Enter(data IRuntimeBTData, stateIn NodeState, localDataIn interface{}) (childNode INode, state NodeState, localDataOut interface{}) {
	switch stateIn {
	case BEGIN:
		return self.children[0], RUNNING, nil

	case SUCCESS:
		return nil, FAILURE, nil

	case FAILURE:
		return nil, SUCCESS, nil
	}
	return
}

///////////////////////////////////////////////////////////////
type SuccessNoCond struct {
	*BaseNode
}

func NewSuccessNoCond() *SuccessNoCond {
	rlt := &SuccessNoCond{
		BaseNode: NewBaseNode(fmt.Sprintf("SuccessNoCond%d", GetNextId("SuccessNoCond")), NodeType_SuccessNoCond),
	}

	rlt.CheckValidationFunc = rlt.checkChild

	return rlt
}

func (self *SuccessNoCond) checkChild() (rlt bool, errInfo string) {
	rlt, errInfo = self.checkChildCnt(1)
	return
}

func (self *SuccessNoCond) Enter(data IRuntimeBTData, stateIn NodeState, localDataIn interface{}) (childNode INode, state NodeState, localDataOut interface{}) {
	switch stateIn {
	case BEGIN:
		return self.children[0], RUNNING, nil

	case SUCCESS, FAILURE:
		return nil, SUCCESS, nil
	}
	return
}

//////////////////////////////////////////////////////
type Counter struct {
	*BaseNode
}

func NewCounter(name string) *Counter {
	rlt := &Counter{
		BaseNode: NewBaseNode(name, NodeType_Counter),
	}

	rlt.CheckValidationFunc = rlt.checkChild

	return rlt
}

func (self *Counter) checkChild() (rlt bool, errInfo string) {
	rlt, errInfo = self.checkChildCnt(1)
	return
}

func (self *Counter) Enter(data IRuntimeBTData, stateIn NodeState, localDataIn interface{}) (childNode INode, state NodeState, localDataOut interface{}) {
	switch stateIn {
	case BEGIN:
		obj := data.GetData(self.GetName())
		if obj == nil {
			panic("counter(" + self.GetName() + ") limit number is nil")
		}
		countLimit := obj.(int32)
		if countLimit <= 0 {
			return nil, SUCCESS, nil
		}
		return self.children[0], RUNNING, int32(0)

	case SUCCESS:
		curCnt := localDataIn.(int32)
		curCnt++

		obj := data.GetData(self.GetName())
		if obj == nil {
			panic("counter(" + self.GetName() + ") limit number is nil")
		}
		countLimit := obj.(int32)
		if curCnt > 0 && curCnt < countLimit {
			return self.children[0], RUNNING, curCnt
		} else {
			return nil, SUCCESS, nil
		}

	case FAILURE:
		return nil, FAILURE, nil
	}
	return
}

///////////////////////////////////////////////////
type CoolDown struct {
	*BaseNode

	cd            int64
	noCDOnFirstIn bool
}

func NewCoolDown(cd time.Duration, noCDOnFirstIn bool) *CoolDown {
	rlt := &CoolDown{
		BaseNode: NewBaseNode(fmt.Sprintf("CD%d:%s", GetNextId("CoolDown"), cd), NodeType_CoolDown),
		cd:       int64(cd),
	}

	rlt.noCDOnFirstIn = noCDOnFirstIn

	rlt.CheckValidationFunc = rlt.checkChild

	return rlt
}

func (self *CoolDown) checkChild() (rlt bool, errInfo string) {
	rlt, errInfo = self.checkChildCnt(1)
	return
}

func (self *CoolDown) Enter(data IRuntimeBTData, stateIn NodeState, localDataIn interface{}) (childNode INode, state NodeState, localDataOut interface{}) {
	now := time.Now().UnixNano()
	switch stateIn {
	case BEGIN:
		lastTimepin := int64(0)
		obj := data.GetData(self.GetName())
		if obj != nil {
			lastTimepin = obj.(int64)
		}
		if lastTimepin == 0 {
			if self.noCDOnFirstIn {
				lastTimepin = now - self.cd
			} else {
				lastTimepin = now
			}
			data.SetData(self.GetName(), lastTimepin)
		}

		if now-lastTimepin >= self.cd {
			data.SetData(self.GetName(), now)
			return self.children[0], RUNNING, nil
		} else {
			return nil, SUCCESS, nil
		}

	case SUCCESS, FAILURE:
		return nil, stateIn, nil
	}
	return
}

///////////////////////////////////////////////////
type UntilFail struct {
	*BaseNode
}

func NewUntilFail() *UntilFail {
	rlt := &UntilFail{
		BaseNode: NewBaseNode(fmt.Sprintf("UntilFail%d", GetNextId("UntilFail")), NodeType_UntilFail),
	}

	rlt.CheckValidationFunc = rlt.checkChild

	return rlt
}

func (self *UntilFail) checkChild() (rlt bool, errInfo string) {
	rlt, errInfo = self.checkChildCnt(1)
	return
}

func (self *UntilFail) Enter(data IRuntimeBTData, stateIn NodeState, localDataIn interface{}) (childNode INode, state NodeState, localDataOut interface{}) {
	switch stateIn {
	case BEGIN:
		return self.children[0], RUNNING, nil

	case SUCCESS:
		return self.children[0], RUNNING, nil

	case FAILURE:
		return nil, SUCCESS, nil
	}
	return
}

///////////////////////////////////////////////////
type Pass struct {
	*BaseNode
}

func NewPass(name string) *Pass {
	rlt := &Pass{
		BaseNode: NewBaseNode(name, NodeType_Pass),
	}

	rlt.CheckValidationFunc = rlt.checkChild

	return rlt
}

func (self *Pass) checkChild() (rlt bool, errInfo string) {
	rlt, errInfo = self.checkChildCnt(1)
	return
}

func (self *Pass) Enter(data IRuntimeBTData, stateIn NodeState, localDataIn interface{}) (childNode INode, state NodeState, localDataOut interface{}) {
	switch stateIn {
	case BEGIN:
		return self.children[0], RUNNING, nil

	case SUCCESS, FAILURE:
		return nil, stateIn, nil
	}
	return
}

////////////////////////////////////////
type DataSrc struct {
	*BaseNode
}

func NewDataSrc(name string) *DataSrc {
	rlt := &DataSrc{
		BaseNode: NewBaseNode(name, NodeType_DataSrc),
	}

	rlt.CheckValidationFunc = rlt.checkChild

	return rlt
}

func (self *DataSrc) checkChild() (rlt bool, errInfo string) {
	rlt, errInfo = self.checkChildCnt(1)
	return
}

func (self *DataSrc) Enter(data IRuntimeBTData, stateIn NodeState, localDataIn interface{}) (childNode INode, state NodeState, localDataOut interface{}) {
	switch stateIn {
	case BEGIN:
		obj := data.GetData(self.GetName())
		if obj == nil {
			panic("dataSrc(" + self.GetName() + ") proc[ func() ] is nil")
		}
		dataFunc := obj.(func())
		dataFunc()
		return self.children[0], RUNNING, nil

	case SUCCESS, FAILURE:
		return nil, stateIn, nil
	}
	return
}
